package edu.hcmus.sow.service;

import java.util.Calendar;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import edu.hcmus.sow.dao.AuthoritiesDAO;
import edu.hcmus.sow.dao.ClientDAO;
import edu.hcmus.sow.dao.GroupDAO;
import edu.hcmus.sow.dao.UserAuthoritiesDAO;
import edu.hcmus.sow.dao.UserDAO;
import edu.hcmus.sow.dao.UserGroupDAO;
import edu.hcmus.sow.dao.UserProfileDAO;
import edu.hcmus.sow.domain.Authorities;
import edu.hcmus.sow.domain.Group;
import edu.hcmus.sow.domain.User;
import edu.hcmus.sow.domain.UserAuthorities;
import edu.hcmus.sow.domain.UserGroup;
import edu.hcmus.sow.domain.UserProfile;
import edu.hcmus.sow.utils.SecurityUtil;

@Service
@Transactional
public class UserService {

   @Autowired
   PasswordEncoder passwordEncoder;

   @Autowired
   private UserDAO userDAO;

   @Autowired
   private UserProfileDAO userProfileDAO;

   @Autowired
   private ClientDAO clientDAO;

   @Autowired
   private GroupDAO groupDAO;

   @Autowired
   private UserGroupDAO userGroupDAO;

   @Autowired
   private AuthoritiesDAO authoritiesDAO;

   @Autowired
   private UserAuthoritiesDAO userAuthoritiesDAO;

   public User saveUser(Integer userId, String username, String password, boolean enabled) {
      User user = userId == null ? new User() : userDAO.findById(userId);
      user.setUsername(username);
      user.setEnabled(enabled);
      if (StringUtils.isNotEmpty(password)) {
         user.setPassword(passwordEncoder.encodePassword(password, null));
      }

      if (userId == null) {
         user.setClient(clientDAO.findById(SecurityUtil.getCurrentClient()));
         user = userDAO.persist(user);
      }
      return user;
   }

   public User saveUser(Integer userId, String username, String password, boolean enabled, Integer clientID) {
      User user = userId == null ? new User() : userDAO.findById(userId);
      user.setUsername(username);
      user.setEnabled(enabled);
      if (StringUtils.isNotEmpty(password)) {
         user.setPassword(passwordEncoder.encodePassword(password, null));
      }

      if (userId == null) {
         user.setClient(clientDAO.findById(clientID));
         user = userDAO.persist(user);
      }
      return user;
   }

   public boolean deleteUser(Integer userID) {
      try {
         User user = userDAO.findById(userID);
         if (user == null)
            return false;
         userDAO.delete(user);
      } catch (Exception e) {
         e.printStackTrace();
         return false;
      }
      return true;
   }

   public UserProfile saveUserProfile(User user, String firstName, String lastName, String middleName, String email,
         String phone) {
      User loggedUser = getUserByID(SecurityUtil.getCurrentLoggedinUserID());
      UserProfile profile = userProfileDAO.getByUserID(user.getId());
      if (profile == null) {
         profile = new UserProfile();
      }

      profile.setFirstName(firstName);
      profile.setLastName(lastName);
      profile.setMiddleName(middleName);
      profile.setEmail(email);
      profile.setPhone(phone);
      profile.setUserByUserId(user);

      profile.setUpdatedDate(Calendar.getInstance());
      profile.setUserByUpdatedBy(loggedUser);

      if (profile.getId() == null) {
         // TODO: set created by
         profile.setCreatedDate(Calendar.getInstance());
         profile.setUserByCreatedBy(loggedUser);
         profile = userProfileDAO.persist(profile);
      }

      return profile;
   }

   public User getUserByID(Integer userID) {
      User user = userDAO.findById(userID);
      return user;
   }

   public UserProfile getUserProfileByUserID(Integer userID) {
      UserProfile up = userProfileDAO.getByUserID(userID);
      return up;
   }

   // ~ Group's methods
   // ================================================================================================
   public List<Group> getGroups(Integer clientID) {
      return groupDAO.getGroups(clientID);
   }

   public Group getGroupByID(Integer groupID) {
      Group group = groupDAO.findById(groupID);
      return group;
   }

   public List<Group> findGroupByName(Integer clientID, String name) {
      return groupDAO.findGroupByName(clientID, name);
   }

   public Group saveGroup(Integer id, String name, String description) {
      User user = getUserByID(SecurityUtil.getCurrentLoggedinUserID());
      Group group = id == null ? new Group() : groupDAO.findById(id);
      group.setName(name);
      group.setDescription(description);

      group.setUpdatedDate(Calendar.getInstance());
      group.setUserByUpdatedBy(user);

      if (id == null) {
         group.setClient(clientDAO.findById(SecurityUtil.getCurrentClient()));
         group.setCreatedDate(Calendar.getInstance());
         group.setUserByCreatedBy(user);
         group.setStatus("A");
         group = groupDAO.persist(group);
      }
      return group;
   }

   public boolean deleteGroup(Integer groupID) {
      try {
         Group group = groupDAO.findById(groupID);
         if (group == null)
            return false;
         groupDAO.delete(group);
      } catch (Exception e) {
         e.printStackTrace();
         return false;
      }
      return true;
   }

   public void updateUserGroups(int userId, int[] groupIds) {
      for (UserGroup userGroup : userGroupDAO.findByUserID(userId)) {
         userGroupDAO.delete(userGroup);
      }
      userGroupDAO.flush();

      User user = userDAO.findById(userId);
      for (int groupId : groupIds) {
         UserGroup userGroup = new UserGroup();
         userGroup.setUser(user);
         userGroup.setGroup(groupDAO.findById(groupId));
         userGroupDAO.persist(userGroup);
      }
   }

   public void addUsersGroup(int groupId, int[] userIds) {
      List<UserGroup> userGroups = userGroupDAO.findByGroup(groupId);

      Group group = groupDAO.findById(groupId);
      for (int userId : userIds) {
         if (!isContains(userGroups, userId)) {
            UserGroup userGroup = new UserGroup();
            userGroup.setUser(userDAO.findById(userId));
            userGroup.setGroup(group);
            userGroupDAO.persist(userGroup);
         }
      }
   }

   public void removeUserFromGroup(int userId, int groupId) {
      UserGroup userGroup = userGroupDAO.findByUserAndGroup(userId, groupId);
      if (userGroup != null)
         userGroupDAO.delete(userGroup);
   }

   public boolean isContains(List<UserGroup> items, int obj) {
      if (items.isEmpty())
         return false;
      for (UserGroup ug : items) {
         if (ug.getUserID() == obj)
            return true;
      }
      return false;
   }

   public Authorities getRole(int userId) {
      List<UserAuthorities> roles = userAuthoritiesDAO.findByUserID(userId);
      if (roles.size() > 0) {
         return roles.get(0).getAuthorities();
      }
      return getDefaultRole();
   }

   public Authorities getDefaultRole() {
      return authoritiesDAO.getAuthority("USER");
   }

   public void updateUserRole(User user, int roleId) {
      Authorities role = authoritiesDAO.findById(roleId);
      List<UserAuthorities> userRoles = userAuthoritiesDAO.findByUserID(user.getId());

      if (userRoles.size() > 0) {
         userRoles.get(0).setAuthorities(role);
      } else {
         UserAuthorities userRole = new UserAuthorities();
         userRole.setAuthorities(role);
         userRole.setUser(user);
         userAuthoritiesDAO.persist(userRole);
      }
   }
}
